/*
 * This file is part of the EasyLogger Library.
 *
 * Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: Portable interface for each platform.
 * Created on: 2015-04-28
 */

#include "os_sys.h"
#include <elog.h>
#include <stdio.h>
#include <time.h>

#ifdef _WIN32
#include <Windows.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <string.h>

static pthread_mutex_t s_log_lock;
static sem_t s_sem_async;
static pthread_t s_task_handle;
#else
#include "Driver_USART.h"
#include "RTE_Device.h"
#include "commontypedef.h"
#include "drv_misc.h"
#include "hlp_board.h"
#include "liot_module_pal.h"
#include "liot_os_pal.h"
#include "liot_uart.h"
#include "osasys.h"

static Liot_MutexId_t s_log_lock = NULL;
static Liot_SemaphoreId_t s_sem_async = NULL;
static Liot_ThreadId_t s_task_handle = NULL;
#endif
#define LOG_TIME_BUFF_SIZE 32
static char s_time_now[LOG_TIME_BUFF_SIZE] = {0};
static BOOL s_task_stop = TRUE;
static char s_log_file_name[256] = {0};
static OS_FILE s_log_file = NULL;
static uint8_t s_log_pos = 1;
#ifdef _WIN32
void *elog_entry(void *para);
#else
void elog_entry(void *para);
#endif

int elog_set_log_output_pos(const char *log_file, uint8_t log_pos) {
  if (log_file && (log_pos == 2 || (log_pos == 3))) {
    uint32_t file_name_len = strlen(log_file);
    if (file_name_len > 256) {
      return -1;
    }
    memset(s_log_file_name, 0, 256);
    strncpy(s_log_file_name, log_file, file_name_len);
  }
  if (log_pos > 3) {
    return -1;
  }
  s_log_pos = log_pos;
  return 0;
}

/**
 * EasyLogger port initialize
 *
 * @return result
 */
ElogErrCode elog_port_init(void) {
  ElogErrCode result = ELOG_NO_ERR;
  s_task_stop = FALSE;
#ifdef _WIN32
  pthread_mutex_init(&s_log_lock, NULL);
  sem_init(&s_sem_async, 0, 1);
  pthread_create(&s_task_handle, NULL, elog_entry, NULL);

  if ((s_log_pos == 2) || (s_log_pos == 3)) {
    if (strlen(s_log_file_name) == 0) {
      sprintf(s_log_file_name, "C:/mcu.log");
    }
    s_log_file = OS_FOPEN(s_log_file_name, "wb");
    if (!s_log_file) {
      return -1;
    }
  }

#else
  liot_uart_config_t cfg;
  int ret = 0;
  cfg.baud = UART_PORT_DEBUG_BAUD;
  cfg.data_bits = UART_PORT_DEBUG_DATABITS;
  cfg.parity = UART_PORT_DEBUG_PARITY;
  cfg.stop_bits = UART_PORT_DEBUG_STOPBITS;

  cfg.pinCfg.rxPin.pinNum = PIN_DEBUG_UART_RX_GPIO_ADDR;
  cfg.pinCfg.rxPin.funcNum = PIN_DEBUG_UART_RX_ALT_FUNX;

  cfg.pinCfg.txPin.pinNum = PIN_DEBUG_UART_TX_GPIO_ADDR;
  cfg.pinCfg.txPin.funcNum = PIN_DEBUG_UART_TX_ALT_FUNX;

  cfg.pinCfg.uartCtsPinEn = false;
  cfg.pinCfg.uartRtsPinEn = false;

  cfg.pinCfg.rxIoMode = IRQ_MODE;
  cfg.pinCfg.txIoMode = POLLING_MODE;
  s_log_lock = Liot_MutexCreate();
  if (!s_log_lock) {
    return ELOG_ERR_UART;
  }
  s_sem_async = Liot_SemaphoreNew(1, 1, NULL);
  if (!s_sem_async) {
    return ELOG_ERR_UART;
  }
  ret = Liot_UartInit(UART_PORT_DEBUG, &cfg);
  if (ret != LIOT_HAL_OK) {
    return ELOG_ERR_UART;
  }
  liot_os_thread_param_t attr;
  memset(&attr, 0x00, sizeof(attr));
  attr.name = "elog_task";
  attr.priority = OS_THREAD_PRIORITY_NORMAL;
  attr.stack_size = 1024;
  s_task_handle = Liot_ThreadCreate(elog_entry, NULL,
                                    (const liot_os_thread_param_t *)&attr);
#endif

  return result;
}

/**
 * EasyLogger port deinitialize
 *
 */
void elog_port_deinit(void) {
#ifdef _WIN32
  s_task_stop = TRUE;
  sem_post(&s_sem_async);
  pthread_join(s_task_handle, NULL);
  pthread_mutex_destroy(&s_log_lock);
  sem_destroy(&s_sem_async);
  if ((s_log_pos == 2) || (s_log_pos == 3)) {
    if (s_log_file) {
      fclose(s_log_file);
      s_log_file = NULL;
    }
  }
#else
  if (s_task_handle) {
    s_task_stop = TRUE;
    Liot_Free(s_task_handle);
    s_task_handle = NULL;
  }
  Liot_UartDeInit(UART_PORT_DEBUG);
  if (s_log_lock) {
    Liot_MutexDestroy(s_log_lock);
    s_log_lock = NULL;
  }
  if (s_sem_async) {
    Liot_SemaphoreDelete(s_sem_async);
    s_sem_async = NULL;
  }
#endif
}

/**
 * output log port interface
 *
 * @param log output of log
 * @param size log size
 */
void elog_port_output(const char *log, size_t size) {
#ifdef _WIN32
  char log_buf[1024] = {0};
  memcpy(log_buf, log, size);
  if ((s_log_pos == 1) || (s_log_pos == 3)) {
    printf("%s", log_buf);
  }
  if ((s_log_pos == 2) || (s_log_pos == 3)) {
    if (s_log_file) {
      fprintf(s_log_file, "%s", log_buf);
    }
  }
#else
  Liot_UartSend(UART_PORT_DEBUG, log, (uint32_t)size);
#endif
}

/**
 * output lock
 */
void elog_port_output_lock(void) {
#ifdef _WIN32
  pthread_mutex_lock(&s_log_lock);
#else
  Liot_MutexLock(s_log_lock);
#endif
}

/**
 * output unlock
 */
void elog_port_output_unlock(void) {
#ifdef _WIN32
  pthread_mutex_unlock(&s_log_lock);
#else
  Liot_MutexUnlock(s_log_lock);
#endif
}

/**
 * get current time interface
 *
 * @return current time
 */
const char *elog_port_get_time(void) {
#ifdef _WIN32
  struct tm *cur_tm_ptr;
  time_t lt;
  lt = time(NULL);
  cur_tm_ptr = localtime(&lt);
  snprintf(s_time_now, LOG_TIME_BUFF_SIZE, "%02d/%02d/%02d,%02d:%02d:%02d:%03d",
           cur_tm_ptr->tm_year + 1900, cur_tm_ptr->tm_mon, cur_tm_ptr->tm_mday,
           cur_tm_ptr->tm_hour, cur_tm_ptr->tm_min, cur_tm_ptr->tm_sec, 0);
#else
  DevTime_t cur_tm;
  DrvMisc_LogTime(&cur_tm);
  snprintf(s_time_now, LOG_TIME_BUFF_SIZE, "%02d/%02d/%02d,%02d:%02d:%02d:%03d",
           cur_tm.year, cur_tm.month, cur_tm.day, cur_tm.hour, cur_tm.minute,
           cur_tm.second, cur_tm.millisecond);
#endif
  return s_time_now;
}

/**
 * get current process name interface
 *
 * @return current process name
 */
const char *elog_port_get_p_info(void) { return NULL; }

/**
 * get current thread name interface
 *
 * @return current thread name
 */
const char *elog_port_get_t_info(void) { return NULL; }

void elog_async_output_notice(void) {
#ifdef _WIN32
  sem_post(&s_sem_async);
#else
  Liot_SemaphoreRelease(s_sem_async);
#endif
}

#ifdef _WIN32
void *elog_entry(void *para) {
#else
void elog_entry(void *para) {
#endif
  size_t get_log_size = 0;
#ifdef ELOG_ASYNC_LINE_OUTPUT
  static char poll_get_buf[ELOG_LINE_BUF_SIZE - 4];
#else
  static char poll_get_buf[ELOG_ASYNC_OUTPUT_BUF_SIZE - 4];
#endif

  while (!s_task_stop) {
    /* waiting log */
#ifdef _WIN32
    sem_wait(&s_sem_async);
#else
    Liot_SemaphoreAcquire(s_sem_async, osWaitForever);
#endif

    /* polling gets and outputs the log */
    while (!s_task_stop) {
#ifdef ELOG_ASYNC_LINE_OUTPUT
      get_log_size =
          elog_async_get_line_log(poll_get_buf, sizeof(poll_get_buf));
#else
      // get_log_size = elog_async_get_log(poll_get_buf, sizeof(poll_get_buf));
#endif
      if (get_log_size) {
        elog_port_output(poll_get_buf, get_log_size);
      } else {
        break;
      }
    }
  }
#ifdef _WIN32
  return NULL;
#endif
}
